package com.yeziji.devops.sql.constructor;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.yeziji.devops.common.expcetion.SqlBuilderException;
import com.yeziji.devops.common.msg.SqlBuilderErrorMsg;
import com.yeziji.devops.constant.SqlExecuteTypeEnum;
import com.yeziji.devops.constant.SqlForeignRelationEnum;
import com.yeziji.devops.constant.mysql.MysqlConstraintTypeEnum;
import com.yeziji.devops.constant.mysql.MysqlKeywordEnum;
import com.yeziji.devops.sql.SqlBuilderFactory;
import com.yeziji.devops.sql.base.SqlConstructorBase;
import com.yeziji.devops.sql.info.AlterAddInfo;
import com.yeziji.devops.sql.info.AlterDropInfo;
import com.yeziji.devops.sql.info.AlterEditInfo;
import com.yeziji.devops.sql.info.IndexKeyInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * ALERT SQL 构造器
 *
 * <p>用于修改数据字段、结构</p>
 *
 * @author gzkemays
 * @since 2023/01/26 11:33 AM
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder(toBuilder = true)
@EqualsAndHashCode(callSuper = true)
public class AlterSqlConstructor extends SqlConstructorBase {
    /**
     * 追加键信息
     */
    private List<AlterAddInfo> add;

    /**
     * 删除键信息
     */
    private List<AlterDropInfo> drop;

    /**
     * 修改键信息
     */
    List<AlterEditInfo> edit;

    /**
     * 追加索引
     */
    List<IndexKeyInfo> index;

    /**
     * 返回 drop 时要根据 mode 进行降序 -> 先执行删除约束、外键、索引、再执行删除字段
     *
     * @return {@link List} mode
     */
    public List<AlterDropInfo> getDrop() {
        if (drop != null) {
            return drop.stream()
                    .sorted(Comparator.comparing(AlterDropInfo::getMode, Comparator.reverseOrder()))
                    .collect(Collectors.toList());
        }
        return null;
    }

    @Override
    protected void check() {
        if (StrUtil.isBlank(super.table)) {
            throw new SqlBuilderException(SqlBuilderErrorMsg.TABLE_IS_EMPTY);
        }
        if (StrUtil.isBlank(super.type)) {
            super.setType(SqlExecuteTypeEnum.ALTER.getValue());
        }
        // add 信息不为空时
        if (CollectionUtil.isNotEmpty(this.add)) {
            for (AlterAddInfo alterAddInfo : this.add) {
                // 对字段进行判断，修改表字段时无论什么操作都涉及字段名
                if (StrUtil.isBlank(alterAddInfo.getColumnName())) {
                    throw new SqlBuilderException(SqlBuilderErrorMsg.COLUMN_EMPTY);
                }
                // 对外键信息的数据结构进行判断
                String referencesTable = alterAddInfo.getReferencesTable();
                String foreignKeyUpdateMethod = alterAddInfo.getForeignKeyUpdateMethod();
                String foreignKeyDeleteMethod = alterAddInfo.getForeignKeyDeleteMethod();
                List<String> referencesColumnName = alterAddInfo.getReferencesColumnName();
                if (CollectionUtil.isNotEmpty(referencesColumnName)) {
                    // 如果没有声明 foreign key column 那么默认指定当前 column
                    if (CollectionUtil.isEmpty(alterAddInfo.getForeignKeyColumnName())) {
                        alterAddInfo.setForeignKeyColumnName(Collections.singletonList(alterAddInfo.getColumnName()));
                    }
                    // 指定了外键字段，说明开始声明外键，对信息进行校验
                    if (StrUtil.isBlank(referencesTable)) {
                        // 指定表为空
                        throw new SqlBuilderException(SqlBuilderErrorMsg.REFERENCES_TABLE_IS_EMPTY);
                    }
                    // 判断引用类型
                    if ((foreignKeyUpdateMethod != null && SqlForeignRelationEnum.hasRelation(foreignKeyUpdateMethod)) ||
                            (foreignKeyDeleteMethod != null && SqlForeignRelationEnum.hasRelation(foreignKeyDeleteMethod))) {
                        throw new SqlBuilderException(SqlBuilderErrorMsg.NOT_SUPPORT_FOREIGN_RELATION);
                    }
                } else if (StrUtil.isNotBlank(referencesTable)) {
                    if (CollectionUtil.isEmpty(alterAddInfo.getForeignKeyColumnName())) {
                        alterAddInfo.setForeignKeyColumnName(Collections.singletonList(alterAddInfo.getColumnName()));
                    }
                    if (CollectionUtil.isEmpty(referencesColumnName)) {
                        throw new SqlBuilderException(SqlBuilderErrorMsg.REFERENCES_COLUMN_IS_EMPTY);
                    }
                    // 判断引用类型
                    if ((foreignKeyUpdateMethod != null && SqlForeignRelationEnum.hasRelation(foreignKeyUpdateMethod)) ||
                            (foreignKeyDeleteMethod != null && SqlForeignRelationEnum.hasRelation(foreignKeyDeleteMethod))) {
                        throw new SqlBuilderException(SqlBuilderErrorMsg.NOT_SUPPORT_FOREIGN_RELATION);
                    }
                }

                // 对约束类型进行判断
                String constraintType = alterAddInfo.getConstraintType();
                String constraintColumnName = alterAddInfo.getConstraintColumnName();
                if (StrUtil.isNotBlank(constraintColumnName) && StrUtil.isBlank(constraintType)) {
                    // 声明约束，但是没有指定约束类型
                    throw new SqlBuilderException(SqlBuilderErrorMsg.CONSTRAINT_TYPE_IS_EMPTY);
                } else if (StrUtil.isNotBlank(constraintType)
                        && StrUtil.isBlank(alterAddInfo.getConstraintColumnName())) {
                    // 声明约束，但是没有指定字段名则默认为 column
                    alterAddInfo.setConstraintColumnName(alterAddInfo.getColumnName());
                } else if (StrUtil.isNotBlank(constraintType)
                        && !MysqlConstraintTypeEnum.supportType(constraintType)) {
                    throw new SqlBuilderException(SqlBuilderErrorMsg.NOT_SUPPORT_CONSTRAINT_TYPE);
                }
            }
        }
        // drop 信息不为空时
        else if (CollectionUtil.isNotEmpty(this.drop)) {
            for (AlterDropInfo dropInfo : this.drop) {
                if (dropInfo.getMode() == null || !AlterDropInfo.MODES.contains(dropInfo.getMode())) {
                    throw new SqlBuilderException(SqlBuilderErrorMsg.EDIT_TYPE_IS_EMPTY);
                }
                if (StrUtil.isBlank(dropInfo.getName())) {
                    throw new SqlBuilderException(SqlBuilderErrorMsg.OPERA_NAME_IS_EMPTY);
                }
            }
        }
        // edit 信息不为空时
        //    else if (CollectionUtil.isNotEmpty(edit)) {
        //      for (AlterEditInfo alterEditInfo : edit) {
        //        if (StrUtil.isNotBlank(alterEditInfo.getColumnRename())) {
        //
        //        }
        //      }
        //    }
        else if (CollectionUtil.isNotEmpty(this.index)) {
            for (IndexKeyInfo info : this.index) {
                if (CollectionUtil.isEmpty(info.getFields())) {
                    throw new SqlBuilderException(SqlBuilderErrorMsg.INDEX_FIELDS_IS_EMPTY);
                }
                if (info.getIndexMethod() != null && MysqlKeywordEnum.getByValue(info.getIndexMethod()) == null) {
                    throw new SqlBuilderException(SqlBuilderErrorMsg.NOT_SUPPORT_CURRENT_INDEX_TYPE);
                }
            }
        }
        super.check();
    }

    @Override
    public String buildSql() {
        if (StrUtil.isNotBlank(this.sql)) {
            return sql;
        }
        this.check();
        return new SqlBuilderFactory().getSqlBuilder(SqlExecuteTypeEnum.ALTER, this).execute();
    }

    public static void main(String[] args) {
        List<AlterAddInfo> add = new ArrayList<>();
        add.add(AlterAddInfo.builder().columnName("nickname").columnType("varchar(255)").build());
        add.add(AlterAddInfo.builder()
                .columnName("id2")
                .columnType("int(11)")
                .comment("年龄")
                .acceptNull(false)
                .defaultValue(0)
                .referencesColumnName(Collections.singletonList("id"))
                .foreignKeyDeleteMethod(SqlForeignRelationEnum.CASCADE.getValue())
                .referencesTable("test2")
                .constraintType(MysqlConstraintTypeEnum.PRIMARY_KEY.getValue())
                .build());
        AlterSqlConstructor build = AlterSqlConstructor.builder().table("test3").add(add).build();
        System.out.println("build = " + JSONUtil.toJsonStr(build));
        System.out.println("build = " + build.buildSql());
    }
}
